﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Globalization;

namespace MvcThemes.ViewEngines
{
    public class ThemeViewEngine : WebFormViewEngine
    {

        public ThemeViewEngine()
        {

            //
            // Define os caminhos possíveis de busca
            //
            base.MasterLocationFormats = new string[] {
                "~/Themes/{2}/Views/{1}/{0}.master",
                "~/Themes/{2}/Views/Shared/{0}.master",
                "~/Themes/Default/Views/{1}/{0}.master",
                "~/Themes/Default/Views/Shared/{0}.master"
            };

            base.ViewLocationFormats = new string[] {
                "~/Themes/{2}/Views/{1}/{0}.aspx",
                "~/Themes/{2}/Views/{1}/{0}.ascx",
                "~/Themes/{2}/Views/Shared/{0}.aspx",
                "~/Themes/{2}/Views/Shared/{0}.ascx",
                "~/Themes/Default/Views/{1}/{0}.aspx",
                "~/Themes/Default/Views/{1}/{0}.ascx",
                "~/Themes/Default/Views/Shared/{0}.aspx",
                "~/Themes/Default/Views/Shared/{0}.ascx"
            };

            base.PartialViewLocationFormats = new string[] {
                "~/Themes/{2}/Views/{1}/{0}.aspx",
                "~/Themes/{2}/Views/{1}/{0}.ascx",
                "~/Themes/{2}/Views/Shared/{0}.aspx",
                "~/Themes/{2}/Views/Shared/{0}.ascx",
                "~/Themes/Default/Views/{1}/{0}.aspx",
                "~/Themes/Default/Views/{1}/{0}.ascx",
                "~/Themes/Default/Views/Shared/{0}.aspx",
                "~/Themes/Default/Views/Shared/{0}.ascx"
            };

        }

        protected override bool FileExists(ControllerContext controllerContext, string virtualPath)
        {
            return System.IO.File.Exists(controllerContext.HttpContext.Server.MapPath(virtualPath));
        }

        public override ViewEngineResult FindView(ControllerContext controllerContext,
            string viewName, string masterName, bool useCache)
        {
            string[] searchedLocationsViewPath;
            string[] searchedLocationsMasterPath;

            string themeName = System.Configuration.ConfigurationManager.AppSettings["Theme"];

            string requiredString = controllerContext.RouteData.GetRequiredString("controller");

            string viewPath = this.GetPath(controllerContext,this.ViewLocationFormats, "ViewLocationFormats",
                    viewName, themeName, requiredString, "View", useCache, out searchedLocationsViewPath);

            string masterPath = this.GetPath(controllerContext,this.MasterLocationFormats, "MasterLocationFormats",
                    masterName, themeName, requiredString, "Master", useCache, out searchedLocationsMasterPath);

            if (!string.IsNullOrEmpty(viewPath) && (!string.IsNullOrEmpty(masterPath) || string.IsNullOrEmpty(masterName)))
                return new ViewEngineResult(this.CreateView(controllerContext, viewPath, masterPath), this);

            return new ViewEngineResult(searchedLocationsViewPath.Union<string>(searchedLocationsMasterPath));
        }

        public override ViewEngineResult FindPartialView(ControllerContext controllerContext,
            string partialViewName, bool useCache)
        {
            string[] searchedLocations;

            string themeName = System.Configuration.ConfigurationManager.AppSettings["Theme"];

            string requiredString = controllerContext.RouteData.GetRequiredString("controller");

            string partialViewPath = this.GetPath(controllerContext, this.PartialViewLocationFormats,
                    "PartialViewLocationFormats", partialViewName, themeName, requiredString, "Partial", useCache,
                    out searchedLocations);

            if (string.IsNullOrEmpty(partialViewPath))
                return new ViewEngineResult(searchedLocations);

            return new ViewEngineResult(this.CreatePartialView(controllerContext, partialViewPath), this);
        }

        private string GetPath(ControllerContext controllerContext,
            string[] locations, string locationsPropertyName,
            string name, string themeName, string controllerName,
            string cacheKeyPrefix, bool useCache, out string[] searchedLocations)
        {
            searchedLocations = null;

            if (string.IsNullOrEmpty(name))
                return string.Empty;

            bool flag = IsSpecificPath(name);

            string key = this.CreateCacheKey(
                cacheKeyPrefix, name, flag ? string.Empty : controllerName, themeName);

            if (useCache)
            {
                string viewLocation = this.ViewLocationCache.GetViewLocation(controllerContext.HttpContext, key);

                if (viewLocation != null)
                    return viewLocation;
            }

            if (!flag)
                return this.GetPathFromGeneralName(controllerContext, locations, name, controllerName, themeName,
                    key, ref searchedLocations);

            return this.GetPathFromSpecificName(controllerContext, name, key, ref searchedLocations);
        }

        private static bool IsSpecificPath(string name)
        {
            char ch = name[0];
            if (ch != '~')
                return (ch == '/');
            return true;
        }

        private string CreateCacheKey(string prefix,
            string name, string controllerName, string themeName)
        {
            return string.Format(CultureInfo.InvariantCulture, ":ViewCacheEntry:{0}:{1}:{2}:{3}:{4}",
                new object[] { base.GetType().AssemblyQualifiedName, prefix, name, controllerName, themeName });
        }

        private string GetPathFromGeneralName(ControllerContext controllerContext, string[] locations, string name,
            string controllerName, string themeName, string cacheKey, ref string[] searchedLocations)
        {
            string virtualPath = string.Empty;
            searchedLocations = new string[locations.Length];

            for (int i = 0; i < locations.Length; i++)
            {

                string fileName = string.Format(CultureInfo.InvariantCulture, locations[i],
                    new object[] { name, controllerName, themeName });

                if (this.FileExists(controllerContext, fileName))
                {
                    searchedLocations = null;
                    virtualPath = fileName;
                    this.ViewLocationCache.InsertViewLocation(
                        controllerContext.HttpContext, cacheKey, virtualPath);
                    return virtualPath;
                }

                searchedLocations[i] = fileName;

            }

            return virtualPath;
        }

        private string GetPathFromSpecificName(ControllerContext controllerContext,
            string name, string cacheKey, ref string[] searchedLocations)
        {
            string virtualPath = name;

            if (!this.FileExists(controllerContext, name))
            {
                virtualPath = string.Empty;
                searchedLocations = new string[] { name };
            }

            this.ViewLocationCache.InsertViewLocation(controllerContext.HttpContext, cacheKey, virtualPath);
            
            return virtualPath;
        }
    }
}